RCrawler101-201605 (Week2)

Leo Lu

(This material is modified from Mansun Kuo’s work)



RCrawler101-201605 (Week2)

2016-05-16

Leo Lu

How to use this slides

Outline

Install: Chrome Extension

Recap Observation Skills and HTTP request for Connection

R Packages Required

Pipeline Coding

Crawler’s toolkits in R

misc

Install Packages

(if you haven’t installed those packages yet)

## === install required packages ===
pkg_list <- c("magrittr", "httr", "rvest", "stringr", "data.table",
              "jsonlite", "RSQLite", "devtools")
pkg_new <- pkg_list[!(pkg_list %in% installed.packages()[,"Package"])]
if(length(pkg_new)) install.packages(pkg_new)
if("xmlview" %in% pkg_new) devtools::install_github("hrbrmstr/xmlview")
rm(pkg_new, pkg_list)

Let’s Rock with R!

Hello RStudio

rstudio

RStudio Settings

Must-known keyboard shortcuts

All RStudio keyboard shortcuts

Description Windows & Linux Mac
Attempt completion / Indent Tab Tab
Run current line/selection Ctrl+Enter +↩︎
Comment/uncomment current line/selection Ctrl+Shift+C ++C
Reindent lines Ctrl+I +I
Insert pipe operator Ctrl+Shift+M ++M

R recap

How to get help

Working Environment

Check your working directory everytime you start to work!

Using getwd/setwd to get/set your working directory.

wd = getwd()
setwd("resources/img")
getwd()
setwd(wd)
getwd()

RStudio will set working directory automatically when opening new files.

If you using Projects, RStudio will change working directory for you automatically.

Basic Data Structure

Vector, Matrix, Array, List and Data frame are the most basic data structure in R. These data structures can be mapped into a table according to:

Homogeneous Heterogeneous
1d Atomic vector List
2d Matrix Data frame
nd Array

(Atomic) Vector

v1 <- c(1:10)
v1
#>  [1]  1  2  3  4  5  6  7  8  9 10
is.vector(v1)
#> [1] TRUE
length(v1)
#> [1] 10
s1 <- 2
s1
#> [1] 2
is.vector(s1)
#> [1] TRUE
length(s1)
#> [1] 1

List

Lists are also vectors, but not atomic vectors. Lists are generic vectors, with (naturally) different semantics.

Elements in a list can be any kinds type and its length is arbitrary.

Function str can help you investigate the structure of a nested list.

li <- list(a = 1:10, 
           b = c("apple", "banana"))
str(li)
#> List of 2
#>  $ a: int [1:10] 1 2 3 4 5 6 7 8 9 10
#>  $ b: chr [1:2] "apple" "banana"
li2 <- list(li = li, 
            c = matrix(1:4, nrow = 2))
str(li2)
#> List of 2
#>  $ li:List of 2
#>   ..$ a: int [1:10] 1 2 3 4 5 6 7 8 9 10
#>   ..$ b: chr [1:2] "apple" "banana"
#>  $ c : int [1:2, 1:2] 1 2 3 4

Visualising lists

x1 <- list(c(1, 2), c(3, 4))
x2 <- list(list(1, 2), list(3, 4))
x3 <- list(1, list(2, list(3)))

Lists Subsetting

a <- list(a = 1:3, b = "a string", c = pi, d = list(-1, -5))
str(a[1:2])
str(a[4])
y <- list("a", 1L, 1.5, TRUE)
str(y[[1]])
str(y[[4]])
a$a
a[["b"]]

Lists Subsetting (Pepper Shaker)

Data Frame

Data frame is a 2-dimension data structure to deal with a table-like heterogeneous data.

df <- data.frame(gender = c("male", "female", "female", "male"),
                 age = c(33, 18, 24, 26))
## Add new column in a data frame
df$city <- c("Taipei", "Taipei", "Hsinchu", "Taichung")
df
#>   gender age     city
#> 1   male  33   Taipei
#> 2 female  18   Taipei
#> 3 female  24  Hsinchu
#> 4   male  26 Taichung
str(df)
#> 'data.frame':    4 obs. of  3 variables:
#>  $ gender: Factor w/ 2 levels "female","male": 2 1 1 2
#>  $ age   : num  33 18 24 26
#>  $ city  : chr  "Taipei" "Taipei" "Hsinchu" "Taichung"

Recap: data structure

All data structures above are objects. They apply different methods and saved as different type internally.

object type class
c(1, 2.5, 3) double numeric
c(“male”, “female”, “female”, “male”) character character
factor(c(“male”, “female”, “female”, “male”)) integer factor
matrix(1:9, nrow = 3) integer matrix
list(a = 1:10, b = c(“apple”, “banana”)) list list
data.frame(a = 1, b = “z”) list data.frame

Function

To understand computations in R, two slogans are helpful:

John Chambers


`+`
#> function (e1, e2)  .Primitive("+")
`<-`
#> .Primitive("<-")
`[`
#> .Primitive("[")
`c`
#> function (..., recursive = FALSE)  .Primitive("c")

Function in R

A typical function in R may look like:

f <- function(par1, par2, ...) {
    # Some magic happened
    return(sth)    # return something
}

Control Flow

If

The basc structure of conditional execution in R is:

if (an expression returns TRUE or FALSE) {
    # do something
} else if (another expression returns TRUE or FALSE) {
    # do something
} else {
    # do something
}

for

Iterate items in R.

# iterate a character vector
for (i in c("a", "b")) {
    print(i)
}
#> [1] "a"
#> [1] "b"
# nested loop
m <- matrix(numeric(), nrow = 2, ncol = 2)
for (i in 1:nrow(m)) {
    for (j in 1:ncol(m)) {
        m[i, j] <- i * j
    }
}
m
#>      [,1] [,2]
#> [1,]    1    2
#> [2,]    2    4

tryCatch

tryCatch({
  result <- expr
  # If you want to use more than one 
  # R expression in the "try" part then you'll have to 
  # use curly brackets.
  # 'tryCatch()' will return the last evaluated expression 
  # in case the "try" part was completed successfully
}, warning = function(w) {
  message("Here's the original warning message:")
  message(w)
  # Choose a return value in case of warning
  return(NULL)
}, error = function(e) {
  message("Here's the original error message:")
  message(e)
  # Choose a return value in case of error
  return(NA)
},
}, finally {
  message("Some other message at the end")
  # finally:
  # Here goes everything that should be executed at the end,
  # regardless of success or error.
  # If you want more than one expression to be executed, then you 
  # need to wrap them in curly brackets ({...}); otherwise you could
  # just have written 'finally=<expression>' 
})

https://stackoverflow.com/questions/12193779/how-to-write-trycatch-in-r/12195574#12195574

magrittr

magrittr logo

What is magrittr

Pipe argument to right-hand side with %>%


Example

library(magrittr)
iris %>% head()
"Ceci n'est pas une pipe" %>% gsub("une", "un", .)

Workflow of Crawler Design

Architecture of Crawlers

Workflow

  1. 找到資料頁,想像資料要長什麼樣子,設想產出的資料格式(schema)
  2. 觀察網頁內容,找到資料所在的request/response,再一層層往上解析,套上判斷式及迴圈, 完成爬蟲的自動化。
  3. 解析取得的資料。

Crawler’s toolkits in R



Web Connector in R

HTTP request

A valid HTTP request includes four things:

Web Connector in R

Connection: GET Method

Use GET() to request data from a specific resource

起手式

## Not Run
library(httr)
library(rvest)
res <- GET(
  url = "http://httpbin.org/get",
  add_headers(a = 1, b = 2),
  set_cookies(c = 1, d = 2),
  query = list(q="hihi")
)
content(res, as = "text", encoding = "UTF-8")
content(res, as = "parsed", encoding = "UTF-8")

一個例子學會第一隻爬蟲

library(magrittr)
library(httr)
library(rvest)
#> Loading required package: xml2
url <- "https://www.ptt.cc/bbs/Gossiping/index.html"
res <- GET(url, 
           set_cookies('over18'='1'))  # over18 cookie
doc <- res %>% 
  content(as = "text", encoding = "UTF-8") %>% 
  read_html

doc %>% 
  html_nodes(css = ".title a") %>% 
  html_text()
#>  [1] "[問卦] 27歲該怎麼從0累積人脈"                  
#>  [2] "Re: [問卦] 要怎麼維持意志力的八卦"             
#>  [3] "[新聞]強國企業家:文革教我們要活下去就要像狼"   
#>  [4] "Re: [新聞] 狼真的來了 印尼2017年底停止輸出幫傭"
#>  [5] "[新聞] 大阪16歲少年駭444學校 只為嘲諷老師沒用" 
#>  [6] "[問卦] 大家跟工作同事相處如何?"                
#>  [7] "Re: [問卦] 生病喝符水,算天擇的一種嘛?"       
#>  [8] "[問卦] 肥宅會做瑜珈有加分嗎"                   
#>  [9] "[新聞] 超越北市!新北YouBike第300站啟用"       
#> [10] "[公告] 八卦板板規(2016.02.16)"                 
#> [11] "Fw: [協尋] 代po漢口路行車紀錄器"               
#> [12] "[協尋] 行車紀錄器-北市仰德大道四段-4/24早上"   
#> [13] "[協尋]5/7行車紀錄器畫面晚上9點屏東東港朝隆路"  
#> [14] "[公告] 五月份置底閒聊文"

httr

Cookies

library(httr)
url <- "https://www.ptt.cc/bbs/Gossiping/index.html"
res <- GET(url, 
           set_cookies('over18'='1'))  # over18 cookie

The response status code

# Get an informative description:
http_status(res)
#> $category
#> [1] "Success"
#> 
#> $reason
#> [1] "OK"
#> 
#> $message
#> [1] "Success: (200) OK"

# Or just access the raw code:
res$status_code
#> [1] 200
status_code(res)
#> [1] 200

# highly recommend using one of these functions whenever you're using httr inside a function to make sure you find out about errors as soon as possible.
warn_for_status(res)
stop_for_status(res)

Connection: POST method

起手式

## Not Run
library(httr)
library(rvest)
res1 <- POST(url = "http://httpbin.org/post", 
             add_headers(a = 1, b = 2),
             set_cookies(c = 1, d = 2),
             body = "A simple text string or raw vector")

res2 <- POST(url = "http://httpbin.org/post",
             add_headers(a = 1, b = 2),
             set_cookies(c = 1, d = 2),
             body = list(x = "A simple text string", 
                         y = "hihi"))

content(res2, as = "text", encoding = "UTF-8")
content(res2, as = "parsed", encoding = "UTF-8")

一個例子學會第二隻爬蟲: Guestbook

Guestbook

Exercise: Guestbook

Try to post a message in App Engine GuestBook

Set header

Sometime you may need to provide appropriate HTTP header fields with add_headers() to make a request.

Let’s see what we got in the
response body

The Response Body

3 ways to access the body of the request:

The Response Body: httr::content()

res <- GET("https://www.ptt.cc/bbs/NBA/M.1463302851.A.8D7.html")
(bin <- content(res, "raw")) %>% head(200) # raw (binary) vector
#>   [1] 3c 21 44 4f 43 54 59 50 45 20 68 74 6d 6c 3e 0a 3c 68 74 6d 6c 3e 0a
#>  [24] 09 3c 68 65 61 64 3e 0a 09 09 3c 6d 65 74 61 20 63 68 61 72 73 65 74
#>  [47] 3d 22 75 74 66 2d 38 22 3e 0a 09 09 0a 0a 3c 6d 65 74 61 20 6e 61 6d
#>  [70] 65 3d 22 76 69 65 77 70 6f 72 74 22 20 63 6f 6e 74 65 6e 74 3d 22 77
#>  [93] 69 64 74 68 3d 64 65 76 69 63 65 2d 77 69 64 74 68 2c 20 69 6e 69 74
#> [116] 69 61 6c 2d 73 63 61 6c 65 3d 31 22 3e 0a 0a 3c 74 69 74 6c 65 3e 5b
#> [139] e8 a8 8e e8 ab 96 5d 20 e6 9e 97 e6 9b b8 e8 b1 aa e5 a6 82 e6 9e 9c
#> [162] e5 8a a0 e5 85 a5 e9 a6 ac e5 88 ba e8 83 bd e5 be 97 e5 88 b0 e5 a4
#> [185] 9a e5 b0 91 e4 b8 8a e5 a0 b4 e6 99 82 e9 96 93
# writeBin(bin, "myfile.txt") # the highest fidelity way of saving files to disk
content(res, "text", encoding = "UTF-8") %>%  # accesses the body as a character vector
  `Encoding<-`("UTF-8")
#> [1] "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<meta charset=\"utf-8\">\n\t\t\n\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\n<title>[討論] 林書豪如果加入馬刺能得到多少上場時間? - 看板 NBA - 批踢踢實業坊</title>\n<meta name=\"robots\" content=\"all\">\n<meta name=\"keywords\" content=\"Ptt BBS 批踢踢\">\n<meta name=\"description\" content=\"\n\n\n最近有傳言說林書豪可能會加入馬刺\n\n\">\n<meta property=\"og:site_name\" content=\"Ptt 批踢踢實業坊\">\n<meta property=\"og:title\" content=\"[討論] 林書豪如果加入馬刺能得到多少上場時間?\">\n<meta property=\"og:description\" content=\"\n\n\n最近有傳言說林書豪可能會加入馬刺\n\n\">\n<link rel=\"canonical\" href=\"https://www.ptt.cc/bbs/NBA/M.1463302851.A.8D7.html\">\n\n<link rel=\"stylesheet\" type=\"text/css\" href=\"//images.ptt.cc/v2.17/bbs-common.css\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"//images.ptt.cc/v2.17/bbs-base.css\" media=\"screen\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"//images.ptt.cc/v2.17/bbs-custom.css\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"//images.ptt.cc/v2.17/pushstream.css\" media=\"screen\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"//images.ptt.cc/v2.17/bbs-print.css\" media=\"print\">\n\n\n<script src=\"//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"></script>\n<script src=\"//images.ptt.cc/v2.17/bbs.js\"></script>\n\n\n\t\t\n\n<script type=\"text/javascript\">\n\n  var _gaq = _gaq || [];\n  _gaq.push(['_setAccount', 'UA-32365737-1']);\n  _gaq.push(['_setDomainName', 'ptt.cc']);\n  _gaq.push(['_trackPageview']);\n\n  (function() {\n    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;\n    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';\n    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);\n  })();\n\n</script>\n\n\n\t</head>\n    <body>\n\t\t\n<div id=\"fb-root\"></div>\n<script>(function(d, s, id) {\nvar js, fjs = d.getElementsByTagName(s)[0];\nif (d.getElementById(id)) return;\njs = d.createElement(s); js.id = id;\njs.src = \"//connect.facebook.net/en_US/all.js#xfbml=1\";\nfjs.parentNode.insertBefore(js, fjs);\n}(document, 'script', 'facebook-jssdk'));</script>\n\n<div id=\"topbar-container\">\n\t<div id=\"topbar\" class=\"bbs-content\">\n\t\t<a id=\"logo\" href=\"/\">批踢踢實業坊</a>\n\t\t<span>&rsaquo;</span>\n\t\t<a class=\"board\" href=\"/bbs/NBA/index.html\"><span class=\"board-label\">看板 </span>NBA</a>\n\t\t<a class=\"right small\" href=\"/about.html\">關於我們</a>\n\t\t<a class=\"right small\" href=\"/contact.html\">聯絡資訊</a>\n\t</div>\n</div>\n<div id=\"navigation-container\">\n\t<div id=\"navigation\" class=\"bbs-content\">\n\t\t<a class=\"board\" href=\"/bbs/NBA/index.html\">返回看板</a>\n\t\t<div class=\"bar\"></div>\n\t\t<div class=\"share\">\n\t\t\t<span>分享</span>\n\t\t\t<div class=\"fb-like\" data-send=\"false\" data-layout=\"button_count\" data-width=\"90\" data-show-faces=\"false\" data-href=\"http://www.ptt.cc/bbs/NBA/M.1463302851.A.8D7.html\"></div>\n\n\t\t\t<div class=\"g-plusone\" data-size=\"medium\"></div>\n<script type=\"text/javascript\">\nwindow.___gcfg = {lang: 'zh-TW'};\n(function() {\nvar po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;\npo.src = 'https://apis.google.com/js/plusone.js';\nvar s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);\n})();\n</script>\n\n\t\t</div>\n\t</div>\n</div>\n<div id=\"main-container\">\n    <div id=\"main-content\" class=\"bbs-screen bbs-content\"><div class=\"article-metaline\"><span class=\"article-meta-tag\">作者</span><span class=\"article-meta-value\">magneto5566 (萬磁王5566_麥寮法斯賓達)</span></div><div class=\"article-metaline-right\"><span class=\"article-meta-tag\">看板</span><span class=\"article-meta-value\">NBA</span></div><div class=\"article-metaline\"><span class=\"article-meta-tag\">標題</span><span class=\"article-meta-value\">[討論] 林書豪如果加入馬刺能得到多少上場時間?</span></div><div class=\"article-metaline\"><span class=\"article-meta-tag\">時間</span><span class=\"article-meta-value\">Sun May 15 17:00:48 2016</span></div>\n\n\n最近有傳言說林書豪可能會加入馬刺\n\n不管對林書豪或對球迷來說馬刺都是個很棒的選擇\n\n馬刺今年例行賽戰績高達67勝,主力陣容維持的話明年應該依舊有60勝左右\n\n雖然波波維奇用兵如神,但林書豪在馬刺到底能獲得多少上場時間呢\n\n林書豪應該是替補PG和SG的位置\n\n單看馬刺和雷霆的系列賽\n\n馬刺的後場部分主要由4位球員分擔時間\n\n先發: Danny Green , Tony Parker\n\n替補: Manu Ginobili, Patty Mills\n\n\n理論上 林書豪最有機會的是Ginobili的位置\n\n畢竟鬼切已經38歲,下季會不會回來都還不確定\n\n林書豪的切入能力雖然沒有巔峰的鬼切犀利\n\n但是大勝38歲鬼切應該是沒問題\n\n相信波波維奇很了解這一點\n\n不知道大家怎麼看呢?\n\n\n--\n<span class=\"f2\">※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 140.112.71.9\n</span><span class=\"f2\">※ 文章網址: <a href=\"https://www.ptt.cc/bbs/NBA/M.1463302851.A.8D7.html\" target=\"_blank\" rel=\"nofollow\">https://www.ptt.cc/bbs/NBA/M.1463302851.A.8D7.html</a>\n</span><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">sss1234     </span><span class=\"f3 push-content\">: 87分鐘</span><span class=\"push-ipdatetime\"> 05/15 17:02\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">→ </span><span class=\"f3 hl push-userid\">h22212247888</span><span class=\"f3 push-content\">: 8.7分鐘</span><span class=\"push-ipdatetime\"> 05/15 17:02\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">→ </span><span class=\"f3 hl push-userid\">rick        </span><span class=\"f3 push-content\">: 等七月再來分析好嗎?</span><span class=\"push-ipdatetime\"> 05/15 17:02\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">demonh311   </span><span class=\"f3 push-content\">: 當然是當基石</span><span class=\"push-ipdatetime\"> 05/15 17:02\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">→ </span><span class=\"f3 hl push-userid\">arcss       </span><span class=\"f3 push-content\">: 前半年冰凍,後半年15分鐘/場</span><span class=\"push-ipdatetime\"> 05/15 17:03\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">→ </span><span class=\"f3 hl push-userid\">hunt5566    </span><span class=\"f3 push-content\">: 呵呵 波波又藏招不讓豪鬼上囉</span><span class=\"push-ipdatetime\"> 05/15 17:04\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">Xenogamer   </span><span class=\"f3 push-content\">: 0</span><span class=\"push-ipdatetime\"> 05/15 17:04\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">love1500274 </span><span class=\"f3 push-content\">: Pop會讓書豪取代跑車先發 書豪會拿下年度mvp</span><span class=\"push-ipdatetime\"> 05/15 17:05\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">→ </span><span class=\"f3 hl push-userid\">littlegreen </span><span class=\"f3 push-content\">: 馬刺需要切入沒錯 但是切傳能力 這書豪比較弱</span><span class=\"push-ipdatetime\"> 05/15 17:06\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">噓 </span><span class=\"f3 hl push-userid\">r30385      </span><span class=\"f3 push-content\">: 8.7秒</span><span class=\"push-ipdatetime\"> 05/15 17:08\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">donnylee    </span><span class=\"f3 push-content\">: Mills也差不多要退了,40歲</span><span class=\"push-ipdatetime\"> 05/15 17:12\n</span></div>         Mills 27歲而已\n<span class=\"f2\">※ 編輯: magneto5566 (140.112.71.9), 05/15/2016 17:14:25\n</span><div class=\"push\"><span class=\"f1 hl push-tag\">→ </span><span class=\"f3 hl push-userid\">darkfeather </span><span class=\"f3 push-content\">: 美媒建議變傳言,這進化速度還真快</span><span class=\"push-ipdatetime\"> 05/15 17:15\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">donnylee    </span><span class=\"f3 push-content\">: 喔,我記錯了,那是哪一個40?</span><span class=\"push-ipdatetime\"> 05/15 17:18\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">Mooooose    </span><span class=\"f3 push-content\">: Popo黑掉的所花的時間會比較多嗎?</span><span class=\"push-ipdatetime\"> 05/15 17:18\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">erotica     </span><span class=\"f3 push-content\">: 阿豪打1 2號都可以  還可以各種大小鎖防守</span><span class=\"push-ipdatetime\"> 05/15 17:21\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">噓 </span><span class=\"f3 hl push-userid\">king181239  </span><span class=\"f3 push-content\">: 去專版問啊</span><span class=\"push-ipdatetime\"> 05/15 17:21\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">→ </span><span class=\"f3 hl push-userid\">king181239  </span><span class=\"f3 push-content\">: 他們會說超過35分鐘</span><span class=\"push-ipdatetime\"> 05/15 17:21\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">→ </span><span class=\"f3 hl push-userid\">erotica     </span><span class=\"f3 push-content\">: 屌打上述4個人</span><span class=\"push-ipdatetime\"> 05/15 17:21\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">→ </span><span class=\"f3 hl push-userid\">king181239  </span><span class=\"f3 push-content\">: 事實上只能出賽20幾</span><span class=\"push-ipdatetime\"> 05/15 17:22\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">噓 </span><span class=\"f3 hl push-userid\">s925407     </span><span class=\"f3 push-content\">: 怎麼會有人說Mills40歲啦,一日球迷也沒這麼不專業</span><span class=\"push-ipdatetime\"> 05/15 17:22\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">→ </span><span class=\"f3 hl push-userid\">s925407     </span><span class=\"f3 push-content\">: 啦</span><span class=\"push-ipdatetime\"> 05/15 17:22\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">a10141013   </span><span class=\"f3 push-content\">: 馬刺大約20-25分鐘 然後popo被說冰箱</span><span class=\"push-ipdatetime\"> 05/15 17:24\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">dream1285201</span><span class=\"f3 push-content\">: 一個球員會黑掉大多跟他的球迷有關</span><span class=\"push-ipdatetime\"> 05/15 17:24\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">smtp        </span><span class=\"f3 push-content\">: 重點是馬刺想出多少爭取豪哥?</span><span class=\"push-ipdatetime\"> 05/15 17:28\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">kl015013    </span><span class=\"f3 push-content\">: 20</span><span class=\"push-ipdatetime\"> 05/15 17:29\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">bycarbird   </span><span class=\"f3 push-content\">: 0,因為書豪不是波波的菜</span><span class=\"push-ipdatetime\"> 05/15 17:30\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">→ </span><span class=\"f3 hl push-userid\">MoMovincent </span><span class=\"f3 push-content\">: 那個說40歲的應該是要說a米吧</span><span class=\"push-ipdatetime\"> 05/15 17:30\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">s95115260   </span><span class=\"f3 push-content\">: 馬刺怎麼可能要球權的球員</span><span class=\"push-ipdatetime\"> 05/15 17:31\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">love1500274 </span><span class=\"f3 push-content\">: 他想說的是Miller 打成Mills  lol</span><span class=\"push-ipdatetime\"> 05/15 17:32\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">kiwi1220    </span><span class=\"f3 push-content\">: 40是 ,Miller</span><span class=\"push-ipdatetime\"> 05/15 17:33\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">miler22020  </span><span class=\"f3 push-content\">: 20分鐘 球權不多 然後popo從此黑掉</span><span class=\"push-ipdatetime\"> 05/15 17:36\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">scatology   </span><span class=\"f3 push-content\">: 5 垃圾時間</span><span class=\"push-ipdatetime\"> 05/15 17:36\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">→ </span><span class=\"f3 hl push-userid\">smtp        </span><span class=\"f3 push-content\">: 10M簽的話, 上場時間不可能只有20分鐘...</span><span class=\"push-ipdatetime\"> 05/15 17:37\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">forker561   </span><span class=\"f3 push-content\">: 第一年不會有上場時間 最快第二年才有上場時間</span><span class=\"push-ipdatetime\"> 05/15 17:38\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">candy9999   </span><span class=\"f3 push-content\">: 去哪都比在黃蜂好</span><span class=\"push-ipdatetime\"> 05/15 17:40\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">DiAbLoE     </span><span class=\"f3 push-content\">: popo會變黑人</span><span class=\"push-ipdatetime\"> 05/15 17:41\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">→ </span><span class=\"f3 hl push-userid\">smtp        </span><span class=\"f3 push-content\">: 豪哥今年曾說,年薪影響別人對你評價,可見企圖心很強</span><span class=\"push-ipdatetime\"> 05/15 17:41\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">happyalex   </span><span class=\"f3 push-content\">: 公道價8分鐘</span><span class=\"push-ipdatetime\"> 05/15 17:43\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">→ </span><span class=\"f3 hl push-userid\">goury       </span><span class=\"f3 push-content\">: 千萬不要去馬刺...Linsanity的重點是明星度,而不是</span><span class=\"push-ipdatetime\"> 05/15 17:47\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">→ </span><span class=\"f3 hl push-userid\">goury       </span><span class=\"f3 push-content\">: 馬刺這樣只在乎勝利,讓買票進場的人看不到球星的隊</span><span class=\"push-ipdatetime\"> 05/15 17:48\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">loverxa     </span><span class=\"f3 push-content\">: 到時不用豪神 就沒人在跟你用兵如神了啦</span><span class=\"push-ipdatetime\"> 05/15 17:49\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">crazywill   </span><span class=\"f3 push-content\">: 馬刺不會要阿豪 不用討論了</span><span class=\"push-ipdatetime\"> 05/15 17:50\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">birdman5656 </span><span class=\"f3 push-content\">: 先等波波帶完奧運吧</span><span class=\"push-ipdatetime\"> 05/15 17:51\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">yyyyy       </span><span class=\"f3 push-content\">: 馬刺應該以我豪為核心進行重建才對</span><span class=\"push-ipdatetime\"> 05/15 17:53\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">leocheng    </span><span class=\"f3 push-content\">: 馬刺喜歡跳投好的 怎麼可能愛豪鬼</span><span class=\"push-ipdatetime\"> 05/15 18:04\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">→ </span><span class=\"f3 hl push-userid\">june0204    </span><span class=\"f3 push-content\">: 還是2000找康利吧</span><span class=\"push-ipdatetime\"> 05/15 18:12\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">→ </span><span class=\"f3 hl push-userid\">HowWhy99    </span><span class=\"f3 push-content\">: 8.7分 不能再多了</span><span class=\"push-ipdatetime\"> 05/15 18:12\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">crazylin924 </span><span class=\"f3 push-content\">: 35分鐘 (一整季</span><span class=\"push-ipdatetime\"> 05/15 18:19\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">噓 </span><span class=\"f3 hl push-userid\">xman262     </span><span class=\"f3 push-content\">: 487秒</span><span class=\"push-ipdatetime\"> 05/15 18:24\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">噓 </span><span class=\"f3 hl push-userid\">ppo7741     </span><span class=\"f3 push-content\">: 8.7分,跟這篇文的分數一樣,不能再高了</span><span class=\"push-ipdatetime\"> 05/15 18:25\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">carey1119   </span><span class=\"f3 push-content\">: 8.7分鐘</span><span class=\"push-ipdatetime\"> 05/15 18:33\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">→ </span><span class=\"f3 hl push-userid\">DurantKevin </span><span class=\"f3 push-content\">: 。。。</span><span class=\"push-ipdatetime\"> 05/15 18:34\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">qq1029qq    </span><span class=\"f3 push-content\">: 87分鐘無誤</span><span class=\"push-ipdatetime\"> 05/15 18:40\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">drew9992    </span><span class=\"f3 push-content\">: <a href=\"http://i.imgur.com/0XBGyQU.jpg\" target=\"_blank\" rel=\"nofollow\">http://i.imgur.com/0XBGyQU.jpg</a></span><span class=\"push-ipdatetime\"> 05/15 19:01\n</span></div><div class=\"richcontent\"><img src=\"//i.imgur.com/0XBGyQU.jpg\" alt=\"\" /></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">dai26       </span><span class=\"f3 push-content\">: 若能完全取代馬妞,大概30分鐘,而且可打滿第四節</span><span class=\"push-ipdatetime\"> 05/15 19:20\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">→ </span><span class=\"f3 hl push-userid\">vicyong     </span><span class=\"f3 push-content\">: 不要在幻想了</span><span class=\"push-ipdatetime\"> 05/15 19:23\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">gotohikaru  </span><span class=\"f3 push-content\">: 第一年不用想</span><span class=\"push-ipdatetime\"> 05/15 19:32\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">waw20002002 </span><span class=\"f3 push-content\">: 8.7分鐘!不能在多了</span><span class=\"push-ipdatetime\"> 05/15 20:13\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">噓 </span><span class=\"f3 hl push-userid\">an565an565  </span><span class=\"f3 push-content\">: 後衛來馬刺都要被冰一整季</span><span class=\"push-ipdatetime\"> 05/15 21:07\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">trylovetom  </span><span class=\"f3 push-content\">: Parker不是被豪哥哥電?</span><span class=\"push-ipdatetime\"> 05/15 21:09\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">→ </span><span class=\"f3 hl push-userid\">smtp        </span><span class=\"f3 push-content\">: Parker是輸在年紀, 巔峰時不可能會被豪哥電~</span><span class=\"push-ipdatetime\"> 05/15 21:12\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">噓 </span><span class=\"f3 hl push-userid\">Yginger1    </span><span class=\"f3 push-content\">: 42分鐘 全場打好打滿</span><span class=\"push-ipdatetime\"> 05/15 21:19\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">pickoff     </span><span class=\"f3 push-content\">: 馬刺不收。問下一隊謝謝</span><span class=\"push-ipdatetime\"> 05/15 21:22\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">mmshlovebob </span><span class=\"f3 push-content\">: 8.7分鐘 不能再多了</span><span class=\"push-ipdatetime\"> 05/15 21:36\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">roc074      </span><span class=\"f3 push-content\">: 8.7時</span><span class=\"push-ipdatetime\"> 05/15 22:04\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">噓 </span><span class=\"f3 hl push-userid\">deathgale   </span><span class=\"f3 push-content\">: 等加盟了再來討論,記者都自以為球團老闆...</span><span class=\"push-ipdatetime\"> 05/15 22:38\n</span></div><div class=\"push\"><span class=\"hl push-tag\">推 </span><span class=\"f3 hl push-userid\">squall410339</span><span class=\"f3 push-content\">: 快去啦馬刺黑掉就是爽</span><span class=\"push-ipdatetime\"> 05/15 23:29\n</span></div><div class=\"push\"><span class=\"f1 hl push-tag\">→ </span><span class=\"f3 hl push-userid\">raku        </span><span class=\"f3 push-content\">: 一開始一場應該是不到10分鐘...</span><span class=\"push-ipdatetime\"> 05/16 00:34\n</span></div></div>\n    \n    <div id=\"article-polling\" data-pollurl=\"/poll/NBA/M.1463302851.A.8D7.html?cacheKey=2052-14787853&offset=7844&offset-sig=05b0d881a2af63a68de9ce2d0f1cda56eec2d548\" data-longpollurl=\"/v1/longpoll?id=5ef93f8aa82d4cf930eb27310d147571cf13936a\" data-offset=\"7844\"></div>\n    \n\n    \n</div>\n\n    </body>\n</html>\n"
content(res, "parsed")
#> {xml_document}
#> <html>
#> [1] <head>\n\t\t<meta charset="utf-8"/>\n\t\t\n\n<meta name="viewport" c ...
#> [2] <body>\n\t\t\n<div id="fb-root"/>\n<script><![CDATA[(function(d, s,  ...

Beware of System Encoding

Encoding

?Encoding ?iconv

## check out your system locale
Sys.getlocale()
#> [1] "en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8"
aa <- "你好嗎"
Encoding(aa)
#> [1] "UTF-8"
charToRaw(aa)
#> [1] e4 bd a0 e5 a5 bd e5 97 8e

(aa_big5 <- iconv(aa, from = "UTF-8", to = "Big5"))
#> [1] "\xa7A\xa6n\xb6\xdc"
Encoding(aa_big5)
#> [1] "unknown"
harToRaw(aa_big5)
#> [1] a7 41 a6 6e b6 dc
# On Windows
Sys.getlocale()
#> "CP950"
aa <- "你好嗎"
Encoding(aa)
#> "unknown"
aa_utf8 <- iconv(aa, from = "Big5", to = "UTF-8")
Encoding(aa_utf8)
#> "UTF-8"

The secret of URL

URL?par1=val1&par2=val2

Query String

You can assign query parameter with query()

res1 <- GET(
  "http://ecshweb.pchome.com.tw/search/v3.3/all/results?q=sony&page=1&sort=rnk/dc"
)

url2 <- "http://ecshweb.pchome.com.tw/search/v3.3/all/results"
res2 <- GET(url2,
           query = list(q="sony", page="1", sort="rnk/dc"))
identical(res1$content, res2$content)
#> [1] TRUE

Url Encoding in R

URLencode(" ")
#> [1] "%20"
greeting = "你好嗎我很好"
greeting_enc = URLencode(greeting)
greeting_enc
#> [1] "%E4%BD%A0%E5%A5%BD%E5%97%8E%E6%88%91%E5%BE%88%E5%A5%BD"
URLdecode(greeting_enc)
#> [1] "你好嗎我很好"

Concatenate strings / String formatting

paste0("hihi", greeting)
#> [1] "hihi你好嗎我很好"
paste0("hihi", greeting, 1:3)
#> [1] "hihi你好嗎我很好1" "hihi你好嗎我很好2" "hihi你好嗎我很好3"
paste("hihi", greeting, 1:3, sep = " ", collapse = ",")
#> [1] "hihi 你好嗎我很好 1,hihi 你好嗎我很好 2,hihi 你好嗎我很好 3"
sprintf("%s,%s嗎?", "hihi", greeting)
#> [1] "hihi,你好嗎我很好嗎?"



Parsing Data

What to Parse

結構化資料

非結構化資料

Parsing Response Content: Text Type

rvest

A web scraper designed to work with magrittr.

Use with rvest

GET(url) %>% content(as="text") %>% rvest::read_html()

Extract elements from HTML document

Tree Structure of HTML (Document Object Model, DOM)

<a href = "www.meetup.com/Taiwan-R">
  Taiwan R User Group Website
</a>

A simple HTML document

demo_page

library(magrittr)
doc = readLines("http://leoluyi.github.io/RCrawler101_201605_Week2/resources/data/demo.html") %>%
    paste(collapse = "\n")
cat(doc)
#> <!DOCTYPE HTML>
#> <html>
#>     <body>
#>         <div id='title' class='character'>
#>             <a class="link" href="http://data-sci.info/r-crawler-101/">data-sci.info</a>
#>         </div>
#>         <div id='summary' class='character'>
#>             <span class='title'>Number:</span>
#>             <span class='number'>2</span>
#>         </div>
#>         <div id='table1' class='info'>
#>             <table style="border-collapse: collapse; border: 1px solid black;">
#>                 <tr>
#>                     <th>Name</th>
#>                     <th>Gender</th>
#>                     <th>Age</th>
#>                 </tr>
#>                 <tr>
#>                     <td>Alice</td>
#>                     <td>Female</td>
#>                     <td>24</td>
#>                 </tr>
#>                 <tr>
#>                     <td>Jane</td>
#>                     <td>Female</td>
#>                     <td>26</td>
#>                 </tr>
#>             </table>
#>         </div>
#>     </body>
#> </html>

Create HTML document object

library(rvest)
doc <- read_html("http://leoluyi.github.io/RCrawler101_201605_Week2/resources/data/demo.html")
doc
#> {xml_document}
#> <html>
#> [1] <body>\n        <div id="title" class="character">\n            <a c ...
class(doc)
#> [1] "xml_document" "xml_node"

Extract with css

CSS practice
http://flukeout.github.io/

doc <- read_html("http://leoluyi.github.io/RCrawler101_201605_Week2/resources/data/demo.html")
doc %>% 
    html_nodes(css = ".character") %>% 
    html_text
#> [1] "\n            data-sci.info\n        "         
#> [2] "\n            Number:\n            2\n        "
doc %>% 
    html_nodes(css = "#title > .link") %>% 
    html_text
#> [1] "data-sci.info"

Extract with xpath

doc <- read_html("http://leoluyi.github.io/RCrawler101_201605_Week2/resources/data/demo.html")
doc %>% 
    html_nodes(xpath = "//*[@class='character']") %>% 
    html_text
#> [1] "\n            data-sci.info\n        "         
#> [2] "\n            Number:\n            2\n        "
doc %>% 
    html_nodes(xpath = "//div[@id='title']/a") %>% 
    html_text
#> [1] "data-sci.info"

Extract name of tag

node = doc %>% 
    html_nodes(css = "#summary") %>% 
    html_name
node
#> [1] "div"

Example: PTT

use package xmlview to parse XML content and extract elements.

Extract table

students <- read_html("http://leoluyi.github.io/RCrawler101_201605_Week2/resources/data/demo.html") %>% 
    html_nodes(xpath = "//table") %>%
    html_table()
students
#> [[1]]
#>    Name Gender Age
#> 1 Alice Female  24
#> 2  Jane Female  26

Extract table

res_text <- GET("https://zh.wikipedia.org/wiki/%E5%9B%BD%E5%AE%B6%E4%BA%BA%E5%8F%A3%E5%88%97%E8%A1%A8") %>% 
  content("text", encoding = "UTF-8") %>% 
  `Encoding<-`("UTF-8")

# create `HTMLInternalDocument` for following use
doc <- XML::htmlParse(res_text, encoding = "UTF-8")
class(doc)
#> [1] "HTMLInternalDocument" "HTMLInternalDocument" "XMLInternalDocument" 
#> [4] "XMLAbstractDocument"
# Parse tables with XML::readHTMLTable()
tables <- XML::readHTMLTable(doc)

# Or you can read table from a html string
tables2 <- res_text %>% XML::readHTMLTable()
identical(tables, tables2)
#> [1] TRUE
# str(tables) # take a look 
# View(tables[[2]]) # take a look 
排名 國家/地區 人口 資料日期 佔世界比例 來源
世界 7,323,900,000 2016年5月1日 100% 美國人口普查局-世界人口時鐘
1 中华人民共和国[註 1] 1,376,400,000 2016年5月1日 18.8% 官方人口時鐘
2 印度 1,325,500,000 2016年5月1日 18.1% 人口時鐘预测
3 美國 323,530,000 2016年5月1日 4.42% 官方人口時鐘
4 印尼 260,460,000 2016年5月1日 3.56% 人口时钟预测
5 巴西 205,870,000 2016年5月1日 2.81% 官方人口時鐘
6 巴基斯坦 193,660,000 2016年5月1日 2.64% 官方人口時鐘
7 奈及利亞 186,360,000 2016年5月1日 2.55% 人口时钟预测
8 孟加拉国 162,650,000 2016年5月1日 2.22% 人口时钟预测
9 俄羅斯[註 2] 146,380,000 2016年5月1日 2% 官方人口時鐘
10 墨西哥 128,550,000 2016年5月1日 1.76% 人口时钟预测
11 日本 126,980,000 2016年4月1日 1.73% 日本統計局
12 菲律賓 103,020,000 2016年5月1日 1.41% 官方人口時鐘
13 衣索比亞 101,550,000 2016年5月1日 1.39% 人口时钟预测
14 越南 94,352,000 2016年5月1日 1.29% 人口时钟预测
15 埃及 90,885,000 2016年5月1日 1.24% 官方人口時鐘
16 德國 81,267,000 2016年5月1日 1.11% 人口时钟预测
17 土耳其 79,884,000 2016年1月1日 1.09% 人口时钟预测
18 刚果(金) 79,346,000 2016年5月1日 1.08% 人口时钟预测
19 伊朗 79,245,000 2016年5月1日 1.08% 官方人口時鐘
20 泰國 68,191,000 2016年5月1日 0.93% 人口时钟预测
21 英國 65,075,000 2016年5月1日 0.89% 人口时钟预测
22 法国[註 3] 64,652,000 2016年5月1日 0.88% 人口时钟预测
23 義大利 59,851,000 2016年5月1日 0.82% 人口时钟预测
24 南非 55,027,000 2016年5月1日 0.75% 人口时钟预测
25 坦桑尼亚 54,907,000 2016年5月1日 0.75% 人口时钟预测
26 緬甸 54,265,000 2016年5月1日 0.74% 人口时钟预测
27 韩国 50,514,000 2016年5月1日 0.69% 人口时钟预测
28 哥伦比亚 48,674,000 2016年5月1日 0.66% 官方人口時鐘
29 肯尼亚 47,096,000 2016年5月1日 0.64% 人口时钟预测
30 西班牙[註 4] 46,061,000 2016年5月1日 0.63% 人口时钟预测
31 阿根廷 43,808,000 2016年5月1日 0.6% 人口时钟预测
32 烏克蘭[註 5] 42,561,000 2016年5月1日 0.58% 人口时钟预测
33 苏丹 40,961,000 2016年5月1日 0.56% 人口时钟预测
34 阿尔及利亚 40,326,000 2016年5月1日 0.55% 人口时钟预测
35 乌干达 40,112,000 2016年5月1日 0.55% 人口时钟预测
36 波蘭 38,626,000 2016年5月1日 0.53% 人口时钟预测
37 伊拉克 37,459,000 2016年5月1日 0.51% 人口时钟预测
38 加拿大 36,267,000 2016年5月1日 0.5% 人口时钟预测
39 摩洛哥[註 6] 34,784,000 2016年5月1日 0.48% 人口时钟预测
40 阿富汗 33,395,000 2016年5月1日 0.46% 人口时钟预测
41 沙烏地阿拉伯 32,196,000 2016年5月1日 0.44% 人口时钟预测
42 秘魯 31,734,000 2016年5月1日 0.43% 人口时钟预测
43 委內瑞拉 31,492,000 2016年5月1日 0.43% 人口时钟预测
44 马来西亚 31,341,000 2016年5月1日 0.43% 官方人口時鐘
45 乌兹别克斯坦 30,285,000 2016年5月1日 0.41% 人口时钟预测
46 尼泊尔 28,800,000 2016年5月1日 0.39% 人口时钟预测
47 莫桑比克 28,647,000 2016年5月1日 0.39% 人口时钟预测
48 加纳 27,980,000 2016年5月1日 0.38% 人口时钟预测
49 葉門 27,435,000 2016年5月1日 0.37% 人口时钟预测
50 安哥拉 25,727,000 2016年5月1日 0.35% 人口时钟预测
51 朝鲜 25,271,000 2016年5月1日 0.35% 人口时钟预测
52 马达加斯加 24,810,000 2016年5月1日 0.34% 人口时钟预测
53 澳大利亚[註 7] 24,070,000 2016年5月1日 0.33% 官方人口時鐘
54 喀麦隆 23,845,000 2016年5月1日 0.33% 人口时钟预测
55 中華民國 23,497,000 2016年3月1日 0.32% 中華民國統計資訊網
56 科特迪瓦 23,162,000 2016年5月1日 0.32% 人口时钟预测
57 斯里蘭卡 20,808,000 2016年5月1日 0.28% 人口时钟预测
58 尼日尔 20,564,000 2016年5月1日 0.28% 人口时钟预测
59 羅馬尼亞 19,371,000 2016年5月1日 0.26% 人口时钟预测
60 布吉納法索 18,560,000 2016年5月1日 0.25% 人口时钟预测
61 智利 18,115,000 2016年5月1日 0.25% 人口时钟预测
62 叙利亚 18,064,000 2016年5月1日 0.25% 人口时钟预测
63 马里 18,044,000 2016年5月1日 0.25% 人口时钟预测
64 哈萨克斯坦 17,867,000 2016年5月1日 0.24% 人口时钟预测
65 馬拉威 17,661,000 2016年5月1日 0.24% 人口时钟预测
66 荷蘭 16,977,000 2016年5月1日 0.23% 官方人口時鐘
67 危地马拉 16,636,000 2016年5月1日 0.23% 人口时钟预测
68 尚比亞 16,628,000 2016年5月1日 0.23% 人口时钟预测
69 厄瓜多尔 16,363,000 2016年5月1日 0.22% 人口时钟预测
70 辛巴威 15,887,000 2016年5月1日 0.22% 人口时钟预测
71 柬埔寨 15,794,000 2016年5月1日 0.22% 人口时钟预测
72 塞内加尔 15,528,000 2016年5月1日 0.21% 人口时钟预测
73 乍得 14,433,000 2016年5月1日 1.97‰ 人口时钟预测
74 几内亚 12,900,000 2016年5月1日 1.76‰ 人口时钟预测
75 南蘇丹 12,798,000 2016年5月1日 1.75‰ 人口时钟预测
76 卢旺达 11,851,000 2016年5月1日 1.62‰ 人口时钟预测
77 布隆迪 11,496,000 2016年5月1日 1.57‰ 人口时钟预测
78 古巴 11,407,000 2016年5月1日 1.56‰ 人口时钟预测
79 比利時 11,365,000 2016年5月1日 1.55‰ 人口时钟预测
80 突尼西亞 11,363,000 2016年5月1日 1.55‰ 人口时钟预测
81 贝宁 11,132,000 2016年5月1日 1.52‰ 人口时钟预测
82 索馬利亞[註 8] 10,995,000 2016年5月1日 1.5‰ 人口时钟预测
83 希臘 10,918,000 2016年5月1日 1.49‰ 人口时钟预测
84 玻利维亚 10,869,000 2016年5月1日 1.48‰ 人口时钟预测
85 海地 10,840,000 2016年5月1日 1.48‰ 人口时钟预测
86 多米尼加 10,643,000 2016年5月1日 1.45‰ 人口时钟预测
87 捷克 10,553,000 2016年5月1日 1.44‰ 人口时钟预测
88 葡萄牙 10,313,000 2016年5月1日 1.41‰ 人口时钟预测
89 阿塞拜疆 9,874,900 2016年5月1日 1.35‰ 人口时钟预测
90 瑞典 9,851,200 2016年5月1日 1.35‰ 人口时钟预测
91 匈牙利 9,829,100 2016年5月1日 1.34‰ 人口时钟预测
92 白俄羅斯 9,499,200 2016年5月1日 1.3‰ 人口时钟预测
93 阿联酋 9,356,500 2016年5月1日 1.28‰ 人口时钟预测
94 塔吉克斯坦 8,617,000 2016年1月1日 1.18‰ 人口时钟预测
95 宏都拉斯 8,552,400 2016年1月1日 1.17‰ 人口时钟预测
96 奥地利 8,441,500 2016年1月1日 1.15‰ 人口时钟预测
97 以色列 8,424,400 2016年1月1日 1.15‰ 人口时钟预测
98 瑞士 8,064,300 2016年1月1日 1.1‰ 人口时钟预测
99 巴布亚新几内亚 7,753,900 2016年1月1日 1.06‰ 人口时钟预测
100 多哥 7,408,300 2016年1月1日 1.01‰ 人口时钟预测
101 香港 7,304,100 2016年1月1日 1‰ 香港政府統計處網站
102 老挝 7,105,200 2016年1月1日 0.97‰ 人口时钟预测
103 塞爾維亞[註 9] 7,090,000 2016年1月1日 0.97‰ 人口时钟预测
104 保加利亚 7,079,800 2016年1月1日 0.97‰ 人口时钟预测
105 巴拉圭 7,037,500 2016年1月1日 0.96‰ 人口时钟预测
106 约旦 6,888,700 2016年1月1日 0.94‰ 人口时钟预测
107 厄立特里亚 6,760,400 2016年1月1日 0.92‰ 人口时钟预测
108 利比亞 6,679,000 2016年1月1日 0.91‰ 人口时钟预测
109 塞拉利昂 6,535,100 2016年1月1日 0.89‰ 人口时钟预测
110 薩爾瓦多 6,377,900 2016年1月1日 0.87‰ 人口时钟预测
111 尼加拉瓜 6,257,200 2016年1月1日 0.85‰ 人口时钟预测
112 吉尔吉斯斯坦 5,934,400 2016年1月1日 0.81‰ 人口时钟预测
113 丹麥 5,648,000 2016年1月1日 0.77‰ 人口时钟预测
114 芬兰[註 10] 5,491,500 2016年1月1日 0.75‰ 官方人口時鐘
115 新加坡 5,488,500 2016年1月1日 0.75‰ 人口时钟预测
116 斯洛伐克 5,432,900 2016年1月1日 0.74‰ 人口时钟预测
117 土库曼斯坦 5,412,600 2016年1月1日 0.74‰ 人口时钟预测
118 挪威[註 11] 5,085,500 2016年1月7日 0.69‰ 人口时钟预测
119 哥斯达黎加 5,061,900 2016年1月1日 0.69‰ 人口时钟预测
120 中非 4,926,400 2016年1月1日 0.67‰ 人口时钟预测
121 刚果(布) 4,850,500 2016年1月1日 0.66‰ 人口时钟预测
122 愛爾蘭 4,842,100 2016年1月1日 0.66‰ 人口时钟预测
123 利比里亚 4,749,100 2016年1月1日 0.65‰ 人口时钟预测
124 巴勒斯坦[註 12] 4,705,200 2016年1月1日 0.64‰ 人口时钟预测
125 新西蘭 4,648,500 2016年1月1日 0.63‰ 官方人口時鐘
126 黎巴嫩 4,468,300 2016年1月1日 0.61‰ 人口时钟预测
127 格鲁吉亚[註 13] 4,432,500 2016年1月1日 0.61‰ 人口时钟预测
128 克罗地亚 4,254,600 2016年1月1日 0.58‰ 人口时钟预测
129 毛里塔尼亚 4,165,800 2016年1月1日 0.57‰ 人口时钟预测
130 巴拿马 4,025,600 2016年1月1日 0.55‰ 人口时钟预测
131 波赫 3,835,300 2016年1月1日 0.52‰ 人口时钟预测
132 波多黎各 3,704,500 2016年1月1日 0.51‰ 人口时钟预测
133 阿曼 3,590,500 2016年1月1日 0.49‰ 人口时钟预测
134 摩尔多瓦[註 14] 3,549,400 2016年1月1日 0.48‰ 人口时钟预测
135 科威特 3,533,300 2016年1月1日 0.48‰ 人口时钟预测
136 乌拉圭 3,426,800 2016年1月1日 0.47‰ 人口时钟预测
137 阿尔巴尼亚 3,195,900 2016年1月1日 0.44‰ 人口时钟预测
138 蒙古 3,060,100 2016年1月1日 0.42‰ 官方人口時鐘
139 亞美尼亞 2,976,600 2016年1月1日 0.41‰ 人口时钟预测
140 立陶宛 2,954,900 2016年1月1日 0.4‰ 人口时钟预测
141 牙买加 2,788,000 2016年1月1日 0.38‰ 人口时钟预测
142 纳米比亚 2,339,400 2016年1月1日 0.32‰ 人口时钟预测
143 博茨瓦纳 2,140,100 2016年1月1日 0.29‰ 人口时钟预测
144 馬其頓 2,126,600 2016年1月1日 0.29‰ 人口时钟预测
145 卡塔尔 2,117,900 2016年1月1日 0.29‰ 人口时钟预测
146 賴索托 2,078,900 2016年1月1日 0.28‰ 人口时钟预测
147 斯洛維尼亞 2,043,700 2016年1月1日 0.28‰ 人口时钟预测
148 拉脫維亞 1,986,100 2016年1月1日 0.27‰ 人口时钟预测
149 冈比亚 1,969,300 2016年1月1日 0.27‰ 人口时钟预测
150 科索沃 1,852,400 2016年1月1日 0.25‰ 人口时钟预测
151 几内亚比绍 1,799,900 2016年1月1日 0.25‰ 人口时钟预测
152 加彭 1,767,200 2016年1月1日 0.24‰ 人口时钟预测
153 巴林 1,472,600 2016年1月1日 0.2‰ 人口时钟预测
154 千里達及托巴哥 1,332,700 2016年1月1日 0.182‰ 人口时钟预测
155 模里西斯 1,329,600 2016年1月1日 0.182‰ 人口时钟预测
156 东帝汶 1,309,100 2016年1月1日 0.179‰ 人口时钟预测
157 爱沙尼亚 1,303,600 2016年1月1日 0.178‰ 人口时钟预测
158 斯威士兰 1,291,300 2016年1月1日 0.176‰ 人口时钟预测
159 賽普勒斯[註 15] 1,203,900 2016年1月1日 0.164‰ 人口时钟预测
160 吉布提 939,250 2016年1月1日 0.128‰ 人口时钟预测
161 斐济 902,980 2016年1月1日 0.123‰ 人口时钟预测
162 赤道几内亚 817,240 2016年1月1日 0.112‰ 人口时钟预测
163 科摩罗 798,120 2016年1月1日 0.109‰ 人口时钟预测
164 圭亚那 781,450 2016年1月1日 0.107‰ 人口时钟预测
165 不丹 778,130 2016年1月1日 0.106‰ 人口时钟预测
166 西撒拉威 638,760 2016年1月1日 0.087‰ 人口时钟预测
167 蒙特內哥羅 604,250 2016年1月1日 0.083‰ 人口时钟预测
168 所罗门群岛 600,090 2016年1月1日 0.082‰ 人口时钟预测
169 澳門 576,660 2016年1月1日 0.079‰ 人口时钟预测
170 苏里南 558,200 2016年1月1日 0.076‰ 人口时钟预测
171 卢森堡 555,700 2016年1月1日 0.076‰ 人口时钟预测
172 佛得角 523,670 2016年1月1日 0.072‰ 人口时钟预测
173 文莱 441,220 2016年1月1日 0.06‰ 人口时钟预测
174 馬爾他 425,800 2016年1月1日 0.058‰ 人口时钟预测
175 巴哈马 385,880 2016年1月1日 0.053‰ 人口时钟预测
176 伯利兹 351,560 2016年1月1日 0.048‰ 人口时钟预测
177 馬爾地夫 336,410 2016年1月1日 0.046‰ 人口时钟预测
178 冰島 329,610 2016年1月1日 0.045‰ 人口时钟预测
179 巴巴多斯 287,390 2016年1月1日 0.039‰ 人口时钟预测
180 瓦努阿圖 260,820 2016年1月1日 0.036‰ 人口时钟预测
181 聖多美和普林西比 204,030 2016年1月1日 0.028‰ 人口时钟预测
182 萨摩亚 193,470 2016年1月1日 0.026‰ 人口时钟预测
183 圣卢西亚 183,680 2016年1月1日 0.025‰ 人口时钟预测
184 關島 171,540 2016年1月1日 0.023‰ 人口时钟预测
185 库拉索 154,840 2014年1月1日 0.021‰ Official estimate
186 阿鲁巴 108,400 2016年1月1日 0.015‰ 人口时钟预测
187 聖文森及格瑞那丁 107,940 2016年1月1日 0.015‰ 人口时钟预测
188 格瑞那達 107,830 2016年1月1日 0.015‰ 人口时钟预测
189 汤加 105,970 2016年1月1日 0.014‰ 人口时钟预测
190 基里巴斯 105,920 2016年1月1日 0.014‰ 人口时钟预测
191 美屬維京群島 104,930 2016年1月1日 0.014‰ 人口时钟预测
192 密克羅尼西亞聯邦 102,100 2016年1月1日 0.014‰ 人口时钟预测
193 澤西 100,800 2014年 0.014‰ 泽西岛官方数据
194 安地卡及巴布達 93,758 2016年1月1日 0.013‰ 人口时钟预测
195 塞舌尔 91,689 2016年1月1日 0.013‰ 人口时钟预测
196 马恩岛 88,471 2016年1月1日 0.012‰ 人口时钟预测
197 安道尔 79,403 2016年1月1日 0.011‰ 人口时钟预测
198 多米尼克 72,297 2016年1月1日 0.0099‰ 人口时钟预测
199 百慕大 66,364 2016年1月1日 0.0091‰ 人口时钟预测
200 开曼群岛 63,021 2016年1月1日 0.0086‰ 人口时钟预测
201 根西 62,950 2014年 0.0086‰ 根西岛官方数据
202 美属萨摩亚 57,850 2016年1月1日 0.0079‰ 人口时钟预测
203 格陵兰 56,922 2016年1月1日 0.0078‰ 人口时钟预测
204 马绍尔群岛 56,787 2016年1月1日 0.0078‰ 人口时钟预测
205 圣基茨和尼维斯 55,368 2016年1月1日 0.0076‰ 人口时钟预测
206 法罗群岛 50,361 2016年1月1日 0.0069‰ 人口时钟预测
207 北馬里亞納群島 45,259 2016年1月1日 0.0062‰ 人口时钟预测
208 荷屬聖馬丁 38,429 2014年 0.0052‰ Official estimate
209 列支敦斯登 37,623 2016年1月1日 0.0051‰ 人口时钟预测
210 摩納哥 37,393 2016年1月1日 0.0051‰ 人口时钟预测
211 特克斯和凯科斯群岛 37,191 2016年1月1日 0.0051‰ 人口时钟预测
212 圣马力诺 32,572 2016年1月1日 0.0044‰ 人口时钟预测
213 直布罗陀 29,855 2016年1月1日 0.0041‰ 人口时钟预测
214 英屬維爾京群島 27,674 2016年1月1日 0.0038‰ 人口时钟预测
215 帛琉 21,057 2016年1月1日 0.0029‰ 人口时钟预测
216 安圭拉 16,807 2016年1月1日 0.0023‰ 人口时钟预测
217 庫克群島 13,238 2016年1月1日 0.0018‰ 人口时钟预测
218 瑙鲁 10,500 2016年1月1日 0.0014‰ 人口时钟预测
219 图瓦卢 10,140 2016年1月1日 0.0014‰ 人口时钟预测
220 蒙特塞拉特 5,144 2016年1月1日 0.0007‰ 人口时钟预测
221 聖赫勒拿 4,131 2016年1月1日 0.0006‰ 人口时钟预测
222 福克蘭群島[註 16] 3,000 2015年7月1日 0.0004‰ IMF estimate for 2014
223 纽埃 1,613 2011年9月10日 0.0002‰ UN estimate for 2010
224 托克勞 1,411 2011年8月18日 0.0002‰ UN estimate for 2010
225 梵蒂冈 842 2014年7月 0.0001‰ Official estimate
226 皮特凯恩群岛 56 2014年 0‰ UN estimate

Example: Yahoo Stock

Example: 公開資訊觀測站

Parse XML table

Parse XML table with XML::xmlToDataFrame()

Example

Parse JSON format

Yun can parse JSON with jsonlite in R


Parse JSON with package jsonlite

library(jsonlite)
library(magrittr)
res <- GET("http://ecshweb.pchome.com.tw/search/v3.3/all/results?q=sony&page=1&sort=rnk/dc")
res_df = content(res, as = "text") %>% 
    fromJSON() %>% 
    .$prods     # equivelent to (function(x) {x$prods})
#> No encoding supplied: defaulting to UTF-8.
str(res_df)
#> 'data.frame':    20 obs. of  11 variables:
#>  $ Id         : chr  "DYAI49-A9006HMLV" "DYAD2O-A9006JDQE" "DYAD2R-A9006XKSI" "DYAD2J-A9006I085" ...
#>  $ cateId     : chr  "DYAI49" "DYAD2O" "DYAD2R" "DYAD2J" ...
#>  $ picS       : chr  "/pic/v1/data/item/201509/D/Y/A/I/4/9/sDYAI49-A9006HMLV000_55f2883d1440e.jpg" "/pic/v1/data/item/201510/D/Y/A/D/2/O/sDYAD2O-A9006JDQE000_560e037b08db2.jpg" "/pic/v1/data/item/201604/D/Y/A/D/2/R/sDYAD2R-A9006XKSI000_56fe3f0b20c2e.jpg" "/pic/v1/data/item/201509/D/Y/A/D/2/J/sDYAD2J-A9006I085000_55f7f34abd5c9.jpg" ...
#>  $ picB       : chr  "/pic/v1/data/item/201605/D/Y/A/I/4/9/DYAI49-A9006HMLV000_5739323992285.jpg" "/pic/v1/data/item/201604/D/Y/A/D/2/O/DYAD2O-A9006JDQE000_57198b2dc111b.jpg" "/pic/v1/data/item/201605/D/Y/A/D/2/R/DYAD2R-A9006XKSI000_572ff4b5655b1.jpg" "/pic/v1/data/item/201509/D/Y/A/D/2/J/DYAD2J-A9006I085000_55f7f34aba324.jpg" ...
#>  $ name       : chr  "SONY SmartBand 2 智慧心率監測手環_ SWR12" "SONY Xperia Z5" "SONY Xperia Z5 Premium" "Sony Xperia C5 Ultra E5553 6吋八核自拍機 " ...
#>  $ describe   : chr  "SONY SmartBand 2 智慧心率監測手環_ SWR12" "超值▼送32G卡+行動電源▼SONY Xperia Z5 5.2吋美型防水旗艦機" "▼送32G卡+手機立架+玻璃貼SONY Xperia Z5 Premium" "送保護套+玻璃保貼Sony Xperia C5 Ultra E5553 6吋八核自拍機 " ...
#>  $ price      : int  3680 16900 19900 8890 11900 20900 4680 2240 14080 16777 ...
#>  $ author     : chr  "" "" "" "" ...
#>  $ brand      : chr  "" "" "" "" ...
#>  $ publishDate: chr  "" "" "" "" ...
#>  $ isPick     : int  0 0 0 0 0 0 0 0 0 0 ...
res_list = content(res, as = "parsed")
str(res_list$prods[[1]])

Parse json with loop

res_df2 = data.frame()
for (i in 1:length(res_list$prods)) {
  res_df2 = rbind(res_df2, 
                  data.frame(res_list$prods[[i]], 
                             stringsAsFactors = FALSE))
}
identical(res_df, res_df2)

Useful R functions to combine list of dataframes

Parse Parse list of data.frames with do.call + rbind

res_df3 = data.frame(do.call(rbind, res_list$prods))
identical(res_df, res_df3)

# An ugly data.frame
str(res_df3)

非結構化資料解析: Regular Expression

Package: stringr - str_replace(), str_replace_all() - str_extract(), str_extract_all()

library(stringr)
fruits <- c("one apple", "two pears", "three bananas")
str_replace(fruits, "[aeiou]", "-")
#> [1] "-ne apple"     "tw- pears"     "thr-e bananas"
str_replace_all(fruits, "[aeiou]", "-")
#> [1] "-n- -ppl-"     "tw- p--rs"     "thr-- b-n-n-s"

shopping_list <- c("apples x4", "bag of flour", "bag of sugar", "milk x2")
str_extract(shopping_list, "[a-z]+")
#> [1] "apples" "bag"    "bag"    "milk"
str_extract_all(shopping_list, "[a-z]+")
#> [[1]]
#> [1] "apples" "x"     
#> 
#> [[2]]
#> [1] "bag"   "of"    "flour"
#> 
#> [[3]]
#> [1] "bag"   "of"    "sugar"
#> 
#> [[4]]
#> [1] "milk" "x"

Exercise : 東森房屋

Exercise: 紫外線即時監測資料

行政院環境保護署環境資源資料開放平台提供了一系列的RESTful Api供大家取用,請試著把 紫外線即時監測資料的資料取回來並轉成data.frame

Answer

紫外線即時監測資料



Save your data

Save your data

write.csv

library(jsonlite)
library(httr)
url = "http://ecshweb.pchome.com.tw/search/v3.3/all/results?q=sony&page=1&sort=rnk/dc"
res_df = GET(url) %>% 
    content(res, as = "text") %>% 
    fromJSON() %>% 
    .$prods     # equivelent to (function(x) {x$prods})
write.csv(res_df, "resources/data/pchome.csv", row.names = FALSE)

download.file

To download a file from the Internet. download.file takes advantage of internet utilities such as curl or wget and may fail if you don’t have any of these utilities in your system.

dest_dir = "resources/data/download"
dir.create(dest_dir, showWarnings = FALSE, recursive = TRUE)

# Download whole HTML file
download.file("https://www.r-project.org/", 
              file.path(dest_dir, "r-project.org.html"))

# Download image
download.file("https://www.r-project.org/Rlogo.png",
              file.path(dest_dir, "Rlogo.png"))

list.files(dest_dir)

writeBin

To write binary data to your local disk.

r = GET("http://opendata.epa.gov.tw/webapi/api/rest/datastore/355000000I-000004/?format=json")
# Set as = "raw" to prevent any character encoding
bin = content(r, as = "raw")
writeBin(bin, "resources/data/download/uv.json")

Database

Case study

References